import {
  AfterContentInit,
  ComponentFactoryResolver,
  Directive,
  EventEmitter,
  Input,
  Output,
  ViewContainerRef
} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {MessageService} from 'primeng/components/common/messageservice';
import {ConfirmationService} from 'primeng/api';
import {Table} from 'primeng/table';
import {FilterGroup} from "../api/FilterGroup";
import {ErrorService} from "../api/ErrorService";
import {TableTemplateComponent} from "./table-template.component";
import {Filter} from "../api/Filter";

@Directive({
  selector: '[gnTable]'
})
export class TableDirective implements AfterContentInit {

  selectAll = false;

  @Input() url: string;
  @Input() includes: string[] = [];
  @Input() autoLoad = true;
  @Input() showSelectAll = true;
  @Input() method = 'POST';
  @Input() showSearch = false;
  @Input() showSearchButton = false;
  @Input() showRefreshButton = true;
  @Input() filterGroup: FilterGroup;

  @Output() selectAllChanged = new EventEmitter();
  @Output() onDeleted = new EventEmitter();

  constructor(private httpClient: HttpClient,
              private messageService: MessageService,
              private errorService: ErrorService,
              private confirmationService: ConfirmationService,
              private resolver: ComponentFactoryResolver,
              private container: ViewContainerRef,
              public table: Table) {
    this.table.rows = 10;
    this.table.rowsPerPageOptions = [5, 10, 20];
    this.table.selectionMode = 'multiple';
    this.table.paginator = true;
    this.table.lazyLoadOnInit = false;

    const factory = this.resolver.resolveComponentFactory(TableTemplateComponent);
    const template = this.container.createComponent(factory);
    template.instance.setTable(this);
    this.table.emptyMessageTemplate = template.instance.emptyMessage;
    this.table.paginatorLeftTemplate = template.instance.paginatorLeft;
    this.table.paginatorRightTemplate = template.instance.paginatorRight;
    template.destroy();

    this.table.onLazyLoad.subscribe(() => {
      this.loadData();
    });
    this.table.onRowSelect.subscribe(event => {
      this.rowSelectChange(event);
    });
    this.table.onRowUnselect.subscribe(event => {
      this.rowSelectChange(event);
    });
  }

  ngAfterContentInit(): void {
    this.table.lazy = this.url != null;

    if (this.autoLoad) {
      this.loadData();
    }
  }

  reloadData(): void {
    this.loadData();
    this.cleanSelection();
  }

  private createQueryParameter(): any {
    const sorts = [];

    if (this.table.sortMode === 'single') {
      if (this.table.sortField != null) {
        sorts.push({'field': this.table.sortField, 'order': this.table.sortOrder === 1 ? 'asc' : 'desc'});
      }
    } else if (this.table.multiSortMeta) {
      for (const s of this.table.multiSortMeta) {
        sorts.push({'field': s.field, 'order': s.order});
      }
    }

    if (this.table.filters) {
      if (!this.filterGroup) {
        this.filterGroup = new class implements FilterGroup {
          childGroups: FilterGroup[] = [];
          filters: Filter[] = [];
          type = 'and';
        };
      }
      if (!this.filterGroup.filters) {
        this.filterGroup.filters = [];
      }

      for (const field in this.table.filters) {
        if (field) {
          this.filterGroup.filters.push({
            'field': field,
            'compareType': this.table.filters[field].matchMode,
            'value': this.table.filters[field].value
          });
        }
      }
    }

    return {
      'pageIndex': this.table.first / this.table.rows,
      'pageSize': this.table.rows,
      'sorts': sorts,
      'filterGroup': this.filterGroup,
      'includes': this.includes
    };
  }

  private loadData(): void {
    if (!this.url || this.url.trim() === '') {
      return;
    }

    this.table.loading = true;
    const queryParameter = this.createQueryParameter();

    this.httpClient.request(
      this.method,
      this.url,
      {body: queryParameter}
    ).subscribe((data: any) => {
        this.table.value = data.content;
        this.table.totalRecords = data === null ? 0 : data.totalElements;
        this.table.loading = false;
      },
      (error) => {
        this.table.value = null;
        this.table.totalRecords = 0;
        this.table.loading = false;
        this.errorService.occur({message: '获取数据错误', error: error});
      });
  }

  get hasSelected(): boolean {
    return this.table.selection == null ? false : this.table.selection.length > 0;
  }

  get hasSelectedOne(): boolean {
    return this.table.selection == null ? false : this.table.selection.length === 1;
  }

  get selection(): any {
    if (this.table.selection) {
      return this.table.selection[0];
    } else {
      return null;
    }
  }

  get selectedKeys(): Array<any> {

    const keys: Array<any> = [];

    if (!this.table.dataKey) {
      return keys;
    }

    const keyNames: string[] = this.table.dataKey.split(',');
    if (this.table.selection) {
      for (const row of this.table.selection) {
        if (keyNames.length > 1) {
          const key = {};
          for (const keyName of keyNames) {
            key[keyName] = row[keyName];
            keys.push(key);
          }
        } else {
          keys.push(row[keyNames[0]]);
        }
      }
    }
    return keys;
  }

  private rowSelectChange(event): void {
    for (let i = 0; i < this.table.value.length; i++) {
      if (event.data === this.table.value[i]) {
        if (this.table.isSelected(this.table.value[i])) {
          this.selectAll = false;
          return;
        }
      } else if (!this.table.isSelected(this.table.value[i])) {
        this.selectAll = false;
        return;
      }
    }
    this.selectAll = true;
  }

  selectAllChange(): void {
    this.table.preventSelectionSetterPropagation = false;
    if (this.selectAll) {
      this.table.selection = this.table.value;
    } else {
      this.cleanSelection();
    }
    this.selectAllChanged.emit();
  }

  delete(): void {
    this.deleteData(this.url, this.selectedKeys);
  }

  deleteData(url: string, data: any): void {
    this.confirmationService.confirm({
      message: '删除选中记录?',
      accept: () => {
        this.httpClient.request('delete', url, {body: data})
          .subscribe(() => {
              this.messageService.add({severity: 'success', summary: '成功', detail: '记录已删除'});
              this.loadData();
              this.cleanSelection();
              this.onDeleted.emit();
            },
            (error) => {
              this.errorService.occur({message: '删除错误', error: error, data: this.selectedKeys});
            });
      }
    });
  }

  cleanSelection(): void {
    this.selectAll = false;
    this.table.selection = null;
    this.table.selectionKeys = {};
  }

}
